<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>调度执行</title>
</head>
<body>
  <div id="app"></div>
  <div id="app1"></div>
<script>
function reactive(data) {
  return new Proxy(data, {
    
    get(target, key, receive) {
      const res = Reflect.get(target, key, receive)
      track(target, key)
      return res
    },
    set(target, key, newValue, receive) {
      const res = Reflect.set(target, key, newValue, receive)
      trigger(target, key)
      return res
    }
  })
}
//依赖存储器
const bucket = new WeakMap() //obj->k->set

function track(target, key) {
  if (!activeEffect) return
  let depsMap = bucket.get(target)
  if (!depsMap) {
    depsMap = new Map()
    bucket.set(target, depsMap)
  }
  let deps = depsMap.get(key)
  if (!deps) {
    deps = new Set()
    depsMap.set(key, deps)
  }
  deps.add(activeEffect)
  activeEffect.deps.push(deps)
}

function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  const deps = new Set(effects)
  deps && deps.forEach((fn) => {
    if(fn !== activeEffect) {
      const {scheduler} = fn.options
      if(scheduler) {
        scheduler(fn)
      } else {
        fn()
      }
    }
  })
}

let activeEffect = null
const effectStack = []
function effect(fn, options = {}) {
  const effectFn = ()=>{
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack.length - 1 > 0 && activeEffect[effectStack.length - 1]
    return res //返回副作用函数执行结果
  }
  effectFn.deps = []
  effectFn.options = options
  if(!options.laze) {
    effectFn()
  }
  return effectFn
  
}

function cleanup(effectFn) {
  const {deps} = effectFn
  for(let i = 0; i < deps.length; i++ ) {
    deps[i].delete(effectFn)
  }
  effectFn.deps.length = 0
}

function computed(getter) {
  const effectFn = effect(getter, {
    laze: true,
    scheduler() {
      dirty = false
      trigger(obj, 'value')
    }
  })
  let dirty = false
  let value 
  const obj = {
    get value() {
      if(!dirty) {
        console.log('get value')
        dirty = true
        value =  effectFn()
      }
      track(obj, 'value')
      return value
    }
  }
  return obj
}
    
</script>

 <script>
const obj = reactive({
  foo: 1
})
const comObj = computed(()=> obj.foo)

//懒求值
console.log(comObj.value)
console.log(comObj.value)

//更改后，重新获取值
obj.foo = 2
console.log(comObj.value)

//嵌套effect执行
effect(()=>{
  console.log('嵌套effect', comObj.value)
})
obj.foo = 3 //期待副作用函数重新执行，预期打印 '嵌套effect', 3'
 </script>
</body>
</html>